#ifndef syntaxtree_h__
#define syntaxtree_h__

#include <iosfwd>
#include <utility>
#include <vector>
#include "symbolstable.h"
#include "errors.h"
#include "codegenerator.h"
//#include "parser.h"


namespace CCompiler
{
	class SynExpression;
    //class SymTable;

    class SynNode
    {
    public:
        virtual void generate(Generator &) {}
        virtual void genLvalue(Generator &) {}
        virtual void print(std::ostream &stream, int ident = 0) = 0;
        virtual ~SynNode() {}
    };

    class SynStatement: public SynNode 
    {
    public:
        virtual bool allowDefault() { return false; } /* only switch allows 'default' label, 
        so we will cast statement that allows default to SynStatementDefault, and ask him about 
        does he already have a default statement 
        ## Also this function allows Case, so we won't create allowCase() method */
        virtual bool allowBreak() { return false; }
        virtual bool allowContinue() { return false; }
    };

    class SynExpression: public SynNode
    {
    public:
        ~SynExpression() {}
        virtual SymType *getType(SymTableInterface &tbl) = 0;
        virtual bool isLvalue() { return false; }
    };

    class SynStatementBlock: public SynStatement
    {
        SymTable tbl_;
        std::vector<SynStatement *> content_;
        size_t localsSize_;
    public:
        SynStatementBlock(): localsSize_(0) {}
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
        void localizeVars()
        {
            for(SymTable::iterator it = tbl_.begin(); it != tbl_.end(); ++it)
            {
                if(it->second->getSymbolType() != stVariable) continue;
                Symbol *kill = it->second;
                it->second = new SymLocalVariable(*((SymVariable *)kill), localsSize_);
                localsSize_ += kill->getType()->getSize();
                delete kill;
            }
        }
        void addStatement(SynStatement *statement)
        {
            content_.push_back(statement);
        }        
        SymTable &getSymTable() { return tbl_; }
        ~SynStatementBlock()
        {
            for(std::vector<SynStatement *>::iterator it = content_.begin();
                it != content_.end(); ++it)
                delete *it;
        }
    };

    class SynStatementBreak: public SynStatement 
    {
    public:
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
    };

    class SynStatementConinue: public SynStatement 
    {
    public:
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
    };

    class SynStatementReturn: public SynStatement 
    {
        SynExpression *expr_;
    public:
        SynStatementReturn(SynExpression *expr = NULL): expr_(expr) {}
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
        ~SynStatementReturn() { delete expr_; }
    };

    class SynStatementExpression: public SynStatement
    {
        SynExpression *expr_;
    public:
        SynStatementExpression(SynExpression *expr): expr_(expr) {}
        virtual void generate(Generator &generator);
        virtual void print(std::ostream &stream, int ident = 0);
        ~SynStatementExpression() { delete expr_; }
    };

    class SynStatementFor: public SynStatement
    {
        SynExpression *init_, *condition_, *iter_;
        SynStatement *body_;
    public:
        SynStatementFor(SynExpression *init=NULL, SynExpression *condition=NULL,
            SynExpression *iter=NULL, SynStatement *body=NULL):
            init_(init), condition_(condition), iter_(iter),
            body_(body) {}
        virtual void print(std::ostream &stream, int ident = 0);
        void assignInit(SynExpression *init) { init_ = init; }
        void assignCondition(SynExpression *condition) { condition_ = condition; }
        void assignIter(SynExpression *iter) { iter_ = iter; }
        void assignBody(SynStatement *body) { body_ = body; }
        virtual void generate(Generator &generator);
        virtual bool allowContinue() { return true; }
        virtual bool allowBreak() { return true; }
        ~SynStatementFor()
        {
            delete init_;
            delete condition_;
            delete iter_;
            delete body_;
        }
    };
    class SynStatementDefault: public SynStatement
    {
        SynStatement *statement_;
    public:
        SynStatementDefault(SynStatement *statement): statement_(statement) {}
        void print(std::ostream &stream, int ident = 0);
        ~SynStatementDefault() { delete statement_; }
    };
    class SynStatementCase: public SynStatement
    {
        SynExpression *label_;
        SynStatement *statement_;
    public:
        SynStatementCase(SynStatement *statement, SynExpression *label):
            statement_(statement), label_(label) {}
        void print(std::ostream &stream, int ident = 0);
        ~SynStatementCase()
        {
            delete label_;
            delete statement_;
        }   
    };

    class SynStatementIf: public SynStatement
    {
    protected:
        SynExpression *condition_;
        SynStatement *action_;
    public:
        SynStatementIf(SynExpression *condition, SynStatement *action): condition_(condition), action_(action) {}
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
        ~SynStatementIf() 
        {
            delete condition_;
            delete action_;
        }
    };
    class SynStatementIfWithElse: public SynStatementIf
    {
        SynStatement *elseAction_;
    public:
        SynStatementIfWithElse(SynExpression *condition, SynStatement *action, SynStatement *elseAction):
          SynStatementIf(condition, action), elseAction_(elseAction) {}
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
        ~SynStatementIfWithElse()
        {
            delete elseAction_;
        }
    };

    class SynStatementWhile: public SynStatement
    {
        SynExpression *condition_;
        SynStatement *body_;
    public:
        SynStatementWhile(SynExpression *condition, SynStatement *body):
            condition_(condition), body_(body) {}
        virtual void print(std::ostream &stream, int ident = 0);
        virtual bool allowContinue() { return true; }
        virtual bool allowBreak() { return true; }
        void assignBody(SynStatement *body) 
        {
            body_ = body;
        }
        void assignCondition(SynExpression *condition) 
        {
            condition_ = condition;
        }
        virtual void generate(Generator &generator);
        ~SynStatementWhile()
        {
            delete condition_;
            delete body_;
        }                
    };

    class SynStatementDoWhile: public SynStatement
    {
        SynExpression *condition_;
        SynStatement *body_;
    public:
        SynStatementDoWhile(SynExpression *condition, SynStatement *body):
            condition_(condition), body_(body) {}
        virtual void print(std::ostream &stream, int ident = 0);
        virtual bool allowContinue() { return true; }
        virtual bool allowBreak() { return true; }
        void assignBody(SynStatement *body) 
        {
            body_ = body;
        }
        virtual void generate(Generator &generator);
        void assignCondition(SynExpression *condition) 
        {
            condition_ = condition;
        }
        ~SynStatementDoWhile()
        {
            delete condition_;
            delete body_;
        };
    };
    class SynStatementPrintf: public SynStatement
    {
        std::vector<SynExpression *>args_;
    public:
        SynStatementPrintf(){}
        void add(SynExpression *arg) { args_.push_back(arg); }                          
        virtual void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
        ~SynStatementPrintf()
        {
            for(std::vector<SynExpression*>::iterator i = args_.begin();
                i != args_.end(); ++i)
                delete *i;
        }
    };
 
    class SynStatementSwitch: public SynStatement
    {                       
        SynExpression *condition_;
        SynStatement *body_;
        bool haveDefault_;
    public:
        void setDefault() { haveDefault_ = true; }
        bool haveDefault() { return haveDefault_; }
        virtual bool allowDefault() { return true; }
        virtual bool allowBreak() { return true; }
        SynStatementSwitch(SynExpression *condition, SynStatement *body):
            condition_(condition), body_(body), haveDefault_(false) {}
        void assignBody(SynStatement *body) 
        {
            body_ = body;
        }
        void assignCondition(SynExpression *condition) 
        {
            condition_ = condition;
        }
        virtual void print(std::ostream &stream, int ident = 0);
        ~SynStatementSwitch()
        {
            delete condition_;
            delete body_;
        }
    };

    class SynConst: public SynExpression
    {
    };

    class SynVal: public SynExpression
    {
        std::string identifier_;
    public:
        SynVal(const std::string& identifier): identifier_(identifier) {}
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return tbl.find(identifier_)->getType();
        }
        const std::string &getName() { return identifier_; }
        virtual bool isLvalue() { return true; }
        virtual void genLvalue(Generator &generator);
        virtual void generate(Generator &generator);
        void print(std::ostream &stream, int ident = 0);
    };

    class SynFunctionCall: public SynExpression
    {
        SynExpression *caller_;
        typedef std::list<SynExpression *> ArgList;
        ArgList arguments_;
    public:
        SynFunctionCall(SynExpression *caller): caller_(caller) {}
        void pushArgument(SynExpression *arg)
        {
            arguments_.push_back(arg);
        }
        void print(std::ostream &stream, int ident = 0);
        virtual void generate(Generator &generator);
        virtual SymType *getType(SymTableInterface &tbl)
        {
            SymType *f = caller_->getType(tbl);
            if(f->getSymbolType() == stFunctionType)
                return ((SymFunctionType *)f)->getResultType()->getType();
            else
                return ((SymFunctionType *)((SymFunctionPointer *)f)->getPointed())->getResultType()->getType();
        }
        ~SynFunctionCall()
        {
            for(ArgList::iterator i = arguments_.begin();
                i != arguments_.end(); ++i)
                delete *i;
            delete caller_;
        }
    };

    class SynBinaryOp: public SynExpression
    {
    protected:
        SynExpression *first_;
        SynExpression *second_;
        std::string binOp_;
    public:
        SynBinaryOp(const std::string& binOp, SynExpression *f, SynExpression *s):
          first_(f), second_(s), binOp_(binOp) {}
        virtual SymType *getType(SymTableInterface &tbl);
        virtual void generate(Generator &generator);
        void print(std::ostream &stream, int ident = 0);
        ~SynBinaryOp() { delete first_; delete second_; }
    };

    class SynAssignOp: public SynBinaryOp
    {
    public:
        SynAssignOp(const std::string &assignOp, SynExpression *lval, SynExpression *right):
          SynBinaryOp(assignOp, lval, right) {}
        virtual void generate(Generator &generator);
        virtual SymType *getType(SymTableInterface &tbl);
    };

    class SynDot: public SynBinaryOp
    {
        void genCommon(Generator &generator);
    public:
        SynDot(SynExpression *f, SynExpression*s): SynBinaryOp(".", f, s) {}
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return ((SymTypeStruct *)first_->getType(tbl))->getField(((SynVal *)second_)->getName())->getType();
        }
        virtual void generate(Generator &generator);
        virtual void genLvalue(Generator &generator);
        virtual bool isLvalue() { return true; }
    };

    class SynArrow: public SynBinaryOp
    {
    public:
        SynArrow(SynExpression *f, SynExpression*s): SynBinaryOp("->", f, s) {}
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return ((SymTypeStruct *)((SymTypePointer *)first_->getType(tbl))->getPointed())->
                getField(((SynVal *)second_)->getName())->getType();
        }
        virtual bool isLvalue() { return true; }
    };

    class SynUnaryOp: public SynExpression
    {
    protected:
        SynExpression *first_;
        std::string unOp_;
        bool postfix_;
    public:
        SynUnaryOp(const std::string& unOp, SynExpression *f, bool postfix = false):
          first_(f), unOp_(unOp), postfix_(postfix) {}
        virtual SymType *getType(SymTableInterface &tbl);
        virtual void generate(Generator &g);
        virtual bool isLvalue() { return unOp_ == "*"; }
        void print(std::ostream &stream, int ident = 0);
        ~SynUnaryOp() { delete first_; }
    };

    class SynCast: public SynUnaryOp
    {
        SymType *toType_;
    public:
        SynCast(SymType *toType, SynExpression *expr):
		  SynUnaryOp('(' + toType->getName() + ')', expr), toType_(toType) {}
        virtual void generate(Generator &generator);
        virtual SymType *getType(SymTableInterface &) { return toType_; }
    };

    class SynInt: public SynConst
    {
        int value_;
    public:
        SynInt(int value): value_(value) {}
        virtual void generate(Generator &generator);
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return tbl.find("int")->getType();
        }
        void print(std::ostream &stream, int ident = 0);
    };

    class SynChar: public SynConst
    {
        char value_;
    public:
        SynChar(char value): value_(value) {}
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return tbl.find("char")->getType();
        }
        void print(std::ostream &stream, int ident = 0);
    };

    class SynFloat: public SynConst
    {
        float value_;
    public:
        SynFloat(float value): value_(value) {}
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return tbl.find("float")->getType();
        }
        virtual void generate(Generator &g);
        void print(std::ostream &stream, int ident = 0);
    };

    class SynString: public SynConst
    {
        std::string value_;
    public:
        SynString(const std::string value): value_(value) {}
        virtual SymType *getType(SymTableInterface &tbl)
        {
            return tbl.find("char*")->getType();
        }
        virtual void generate(Generator &generator);
        void print(std::ostream &stream, int ident = 0);
    };

    class SynIndexation: public SynBinaryOp
    {
        void genCommon(Generator &generator);
    public:
        SynIndexation(SynExpression *f, SynExpression *index): SynBinaryOp("[]", f, index) {}
        virtual SymType *getType(SymTableInterface &tbl) { return ((SymTypePointer *)first_->getType(tbl))->getPointed();}
        virtual void generate(Generator &generator);
        virtual void genLvalue(Generator &generator);
        virtual bool isLvalue() { return true; }
    };

    class SynSizeof: public SynExpression
    {
        SymType *type_;
    public:
        SynSizeof(SymType *type): type_(type) {}
        virtual SymType *getType(SymTableInterface &tbl) { return tbl.find("int")->getType(); }
        virtual void generate(Generator &generator);
        void print(std::ostream &stream, int ident = 0);
    };
}

#endif // syntaxtree_h__
